#pragma once

#include "requestor.hpp"
#include "../common/uuid.hpp"

namespace JsonMuduoRPC
{
    namespace client
    {
        class Provider
        {
        public:
            using ptr = std::shared_ptr<Provider>;

            Provider(const Requestor::ptr &requestor) : _requestor(requestor) {}

            // 给客户端提供服务注册请求接口
            bool registryRequest(const BaseConnection::ptr &conn, const std::string &method, const Address &host)
            {
                // 1. 组织请求
                ServiceRequest::ptr service_req = MessageFactory::create<ServiceRequest>();
                service_req->setHost(host);
                service_req->setId(UUID::getUuidStr());
                service_req->setMethod(method);
                service_req->setMType(MType::REQ_SERVICE);
                service_req->setOpType(ServiceOpType::SERVICE_REGISTRY);

                // 2. 通过_requestor发送请求，同步发送
                BaseMessage::ptr msg_rsp;
                bool ret = _requestor->send(conn, service_req, msg_rsp);
                if (ret == false)
                {
                    LOG(ERROR, "服务注册失败");
                    return false;
                }

                // 3. 检查响应
                ServiceResponse::ptr service_rsp = std::dynamic_pointer_cast<ServiceResponse>(msg_rsp);
                if (service_rsp.get() == nullptr)
                {
                    LOG(ERROR, "指针向下转换失败");
                    return false;
                }

                if (service_rsp->getRCode() != RCode::RCODE_OK)
                {
                    LOG(ERROR, "服务注册失败: %s", errReason(service_rsp->getRCode()).c_str());
                    return false;
                }
                return true;
            }

        private:
            Requestor::ptr _requestor;
        };

        // 管理——每个方法可以由哪些主机提供
        class MethodHost
        {
        public:
            using ptr = std::shared_ptr<MethodHost>;

            MethodHost(std::vector<Address> hosts) : _hosts(hosts) {}
            MethodHost() {}

            void appendHost(const Address &host)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _hosts.push_back(host);
            }

            // 负载均衡——RR轮询
            Address chooseHost()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                LOG(INFO, "主机 %d [%s:%d] 提供服务", _index++ % _hosts.size(),
                    _hosts[_index++ % _hosts.size()].first.c_str(),
                    _hosts[_index++ % _hosts.size()].second);

                return _hosts[_index++ % _hosts.size()];
            }

            void removeHosts(const Address &host)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                for (auto it = _hosts.begin(); it != _hosts.end(); ++it)
                {
                    if (*it == host)
                        _hosts.erase(it);
                }
            }

            bool empty()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                return _hosts.empty();
            }

        private:
            std::mutex _mutex;
            std::vector<Address> _hosts;
            size_t _index = 0;
        };

        class Discover
        {
        public:
            using ptr = std::shared_ptr<Discover>;
            using OfflineCallBack = std::function<void(const Address &)>;

            Discover(const Requestor::ptr &requestor, const OfflineCallBack &offline_callback)
                : _requestor(requestor),
                  _offline_callcack(offline_callback) {}

            // 服务发现请求
            bool serviceDiscovery(const BaseConnection::ptr &conn, const std::string &method, Address &host)
            {
                // 1. 如果_methods_map已经保管了method方法，则直接在映射关系中返回一个主机
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    for (auto &it : _methods_map)
                    {
                        if (it.first == method)
                        {
                            MethodHost::ptr method_host = it.second;
                            if (method_host->empty() == false)
                            {
                                host = method_host->chooseHost();
                                return true;
                            }
                        }
                    }
                }

                // 2. 如果没有保管method方法，或者method方法对应的hosts为空，就进行服务发现请求
                ServiceRequest::ptr service_req = MessageFactory::create<ServiceRequest>();
                service_req->setId(UUID::getUuidStr());
                service_req->setMethod(method);
                service_req->setMType(MType::REQ_SERVICE);
                service_req->setOpType(ServiceOpType::SERVICE_DISCOVERY);

                BaseMessage::ptr msg_rsp;
                bool ret = _requestor->send(conn, service_req, msg_rsp);
                if (ret == false)
                {
                    LOG(ERROR, "服务发现失败: 请求发送失败");
                    return false;
                }

                // 获得响应
                // 检查响应
                ServiceResponse::ptr service_rsp = std::dynamic_pointer_cast<ServiceResponse>(msg_rsp);
                if (service_rsp.get() == nullptr)
                {
                    if (ret == false)
                    {
                        LOG(ERROR, "服务发现失败: 响应类型向下转换失败");
                        return false;
                    }
                }
                if (service_rsp->getRCode() != RCode::RCODE_OK)
                {
                    LOG(ERROR, "服务发现失败: %s", errReason(service_rsp->getRCode()).c_str());
                    return false;
                }

                // 根据响应，向_methods_map添加主机
                std::unique_lock<std::mutex> lock(_mutex);
                const std::vector<Address> &hosts = service_rsp->getHosts();
                if (hosts.empty())
                {
                    LOG(ERROR, "服务发现失败: 没有可以提供 %s 服务的主机", method.c_str());
                    return false;
                }
                MethodHost::ptr method_host = std::make_shared<MethodHost>(hosts);
                _methods_map[method] = method_host;
                host = method_host->chooseHost();

                return true;
            }

            // 针对服务上下限通知请求作处理
            void onServiceRequest(const BaseConnection::ptr &conn, const ServiceRequest::ptr &req)
            {
                const std::string &method = req->getMethod();
                std::unique_lock<std::mutex> lock(_mutex);

                // 1. 判断是一个服务上线通知还是一个服务下线通知
                if (req->getOpType() == ServiceOpType::SERVICE_ONLINE)
                {
                    // 服务上线 找到对应的MethodHost，并添加主机，不存在便创建
                    auto it = _methods_map.find(method);
                    if (it == _methods_map.end())
                    {
                        MethodHost::ptr method_host = std::make_shared<MethodHost>();
                        method_host->appendHost(req->getHost());

                        _methods_map.insert(std::make_pair(method, method_host));
                    }
                    else
                        it->second->appendHost(req->getHost());
                }
                else if (req->getOpType() == ServiceOpType::SERVICE_OFFLINE)
                {
                    // 服务下线 找到对应的MethodHost，并删除主机
                    auto it = _methods_map.find(method);
                    if (it != _methods_map.end())
                    {
                        it->second->removeHosts(req->getHost());
                        _offline_callcack(req->getHost());
                    }
                }
            }

        private:
            std::mutex _mutex;
            std::unordered_map<std::string, MethodHost::ptr> _methods_map;
            Requestor::ptr _requestor;
            OfflineCallBack _offline_callcack;
        };
    } // namespace client

}